#include "evaluar.h"

bool igualdad_valida(double a, double b)
{
	return (fabs(a-b) <= epsilon);
}

bool verificar(vPunto& puntos, vSpline& spline, int n)
{	/* Spline natural.
	 * 
	 * S(xj) = f (xj) ∀j, j = 0, . . . , n
	 *   Sj+1(xj+1) =   Sj(xj+1) ∀j, j = 0, . . . , n − 2 (continuidad).
	 *  Sj+1'(xj+1) =  Sj'(xj+1) ∀j, j = 0, . . . , n − 2 (derivadas iguales de ambos lados).
	 * Sj+1''(xj+1) = Sj''(xj+1) ∀j, j = 0, . . . , n − 2 (concavidades iguales de ambos lados).
	 * S''(x0) = 0 y S''(xn) = 0
	*/
	forn(i, n-1)
	{
		/* Que interpole bien. */
		if (!igualdad_valida(spline[i].a, puntos[i].y))
		{
			cerr << "No interpola bien en " << puntos[i].x << " " << puntos[i].y << " i: " << i << endl;
			return false;
		}
	}
		
	forn(i, n-2)
	{
		/* Valores iguales de ambos lados. */
		double punto = spline[i+1].x_i;		
		
		if (!igualdad_valida(evaluar_fijo(punto, spline[i]), evaluar_fijo(punto, spline[i+1])))
		{
			cerr << "Los valores no son iguales de ambos lados " << punto << " " << " i: " << i << endl;
			cerr << evaluar_fijo(punto, spline[i]) << " " << evaluar_fijo(punto, spline[i+1]) << endl;
			return false;
		}
		
		if (!igualdad_valida(evaluar_derivada_primera(punto, spline[i]), evaluar_derivada_primera(punto, spline[i+1])))
		{
			cerr << "Las derivadas primeras no son iguales de ambos lados " << punto << " " << " i: " << i << endl;
			cerr << evaluar_derivada_primera(punto, spline[i]) << " " << evaluar_derivada_primera(punto, spline[i+1]) << endl;
			return false;
		}
			
		if (!igualdad_valida(evaluar_derivada_segunda(punto, spline[i]), evaluar_derivada_segunda(punto, spline[i+1])))
		{
			cerr << "Las derivadas segundas no son iguales de ambos lados " << punto << " " << " i: " << i << endl;
			cerr << evaluar_derivada_segunda(punto, spline[i]) << " " << evaluar_derivada_segunda(punto, spline[i+1]) << endl;
			return false;
		}
	}
	
	/* Spline natural. */
	if (!igualdad_valida(spline[0].c, 0))
	{
		cerr << "No cumple la condición de borde en x_o " << spline[0].c << endl;
		return false;
	}
			
	if (!igualdad_valida(evaluar_derivada_segunda(puntos[n-1].x, spline[n-2]), 0))
	{
		cerr << "No cumple la condición de borde en x_n " << evaluar_derivada_segunda(spline[n-1].max, spline[n-1]) << endl;
		return false;
	}
		
	return true;
}

double evaluar_fijo(double punto, Spline& spline)
{
	/* Recibe el S_i correspondiente y evalua en punto. */
	double res = 0;	
	
	/* d(x-x_i)^3 + c(x-x_i)^2 + b(x-x_i) + a */
	double aux = punto - spline.x_i;	
	res = spline.d * aux * aux * aux + spline.c * aux * aux + spline.b * aux + spline.a;
	
	return res;
}

double evaluar_derivada_primera(double punto, Spline& spline)
{
	/* Recibe el S_i correspondiente y evalua en punto. */
	double res = 0;	
	
	/* 3d(x-x_i)^2 + 2c(x-x_i) + b */
	double aux = punto - spline.x_i;
	res = spline.d * 3 * aux * aux + spline.c * 2 * aux + spline.b;
	
	return res;
}

double evaluar_derivada_segunda(double punto, Spline& spline)
{
	/* Recibe el S_i correspondiente y evalua en punto. */
	double res = 0;	
	
	/* 6d(x-x_i) + 2c */
	double aux = punto - spline.x_i;			
	res = spline.d * 6 * aux + spline.c * 2;
	
	return res;
}

double evaluar(double s, vSpline& spline, int n)
{
	double res = 0;
	
	/* Encontrar el S_i correspondiente y evaluar. */
	forn(i, n-1)
	{
		if (spline[i].min <= s && s <= spline[i].max)
		{
			/* a(x-x_i)^3 + b(x-x_i)^2 + c(x-x_i) + d */
			double aux = s - spline[i].x_i;			
			res = spline[i].d * aux * aux * aux + spline[i].c * aux * aux + spline[i].b * aux + spline[i].a;
			break;
		}
	}
	
	return res;
}

double evaluar_derivada_primera(double s, vSpline& spline, int n)
{
	double res = 0;
	
	/* Encontrar el S_i correspondiente y evaluar. */
	forn(i, n-1)
	{
		if (spline[i].min <= s && s <= spline[i].max)
		{
			res = evaluar_derivada_primera(s, spline[i]);
			break;
		}
	}
	
	return res;
}

double evaluar_derivada_segunda(double s, vSpline& spline, int n)
{
	double res = 0;
	
	/* Encontrar el S_i correspondiente y evaluar. */
	forn(i, n-1)
	{
		if (spline[i].min <= s && s <= spline[i].max)
		{
			res = evaluar_derivada_segunda(s, spline[i]);
			break;
		}
	}
	
	return res;
}


void evaluar(vdouble& muestreo, vPunto& muestreados, vSpline& spline_x, vSpline& spline_y, int n, int m)
{
	double X, Y, s;
	
	forn(i, m)
	{		
		s = muestreo[i];		
		X = evaluar(s, spline_x, n);
		Y = evaluar(s, spline_y, n);				
		
		muestreados[i].x = X;
		muestreados[i].y = Y;		
	}
	
	return;
}
